/**
 * @constructor Archetype.Tools.URL
 */
$N("Archetype.Tools.URL", Archetype.Class.create({

    initialize: function (_urlString) {
        return this.parse(_urlString);
    },

    parse: function(_urlString) {
        this.urlString = _urlString || window.location.href;
        var uriArray = $A(this.urlString.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)([#?]{1}.*)?$/))||[];

        //console.debug("uriArray", uriArray);
        // URI basic parts
        this.scheme = uriArray[2];
        this.authority = uriArray[4];
        this.path = uriArray[5];
        this.queryOrFragment = uriArray[6];

        // URL basic parts
        this.protocol = this.scheme;
        var parsedAuthority = this.authority.match(/^(([^:@]*):?([^@]*?)@)?([^:\/]*)(:([0-9]*?))?$/);
        //console.debug("parsedAuthority",parsedAuthority);
        // foo
        this.login = parsedAuthority[2] ||"";
        // bar
        this.password = parsedAuthority[3] ||"";
        // localhost, 127.0.0.1, archetypejs.org
        this.host = parsedAuthority[4];
        // 80, 443, 8080
        this.port = parsedAuthority[6] !== undefined ? parsedAuthority[6] : (this.protocol=="https" ? "443" : "80");
        // /foo/bar/bar.html
        this.fullPath = parsedAuthority[9] || "";

        var pathInfo = this.fullPath.match(/^(.*\/)(.*?)$/)||[];
        // /foo/bar/
        this.path=pathInfo[1];
        // bar.html
        this.resource=pathInfo[2];

        var urlEnd = this.queryOrFragment || "";
        //this.queryPath=$A(urlEnd.match(/^.*?\?(([^#]+)\/([^#]*))+($|\#.*$)/));
        // ?...
        this.setQueryString ($A(urlEnd.match(/^.*?\?([^#]+)($|\#.*$)/))[1]);
        // #...
        this.setFragmentString($A(urlEnd.match(/^.*?\#([^?]+)($|\?.*$)/))[1]);

        return this;
    },
    setFragmentString: function (fragmentString) {
        this.fragmentString = fragmentString || "";
        this.setFragment (this.fragmentString.toQueryParams());
        this.setFragmentPath (this.fragmentString.split("/"));
        // #bar
        this.fragmentState = this.hasFragment() ? "" : this.fragmentString;
        this.updateAnyState();
        return this;
    },
    setQueryString: function (queryString) {
        this.queryString = queryString || "";
        this.setQuery (this.queryString.toQueryParams());
        this.setQueryPath (this.queryString.split("/"));
        // ?foo
        this.queryState = this.hasQuery() ? "" : this.queryString;
        this.updateAnyState();
        return this;
    },
    setFragment: function (fragment) {
        this.fragment = $H(fragment);
        this.updateAnyParam();
        return this;
    },
    setQuery: function (query) {
        this.query = $H(query);
        this.param = this.query;
        this.updateAnyParam();
        return this;
    },
    setFragmentPath: function (fragmentPath) {
        this.fragmentPath = $A(fragmentPath);
        var fragmentPathParameters = {};
        var fragmentPathParameterArrays =  this.fragmentPath.partition(function(e,i) {
            return 0 == i % 2;
        }) || [[],[]];
        fragmentPathParameterArrays[0].each(function(key,i) {
            if(fragmentPathParameterArrays[1]&& i< fragmentPathParameterArrays[1].length)
                fragmentPathParameters[key] = fragmentPathParameterArrays[1][i]
        });
        this.fragmentPathParameters = fragmentPathParameters;
        this.updateAnyPathParameters();
        return this;
    },
    setQueryPath: function (queryPath) {
        this.queryPath = $A(queryPath);
        var queryPathParameterArrays = this.queryPath.partition(function(e,i) {
            return 0 == i % 2;
        }) || [[],[]];
        var queryPathParameters = {};
        queryPathParameterArrays[0].each(function(key,i) {
            if(queryPathParameterArrays[1] && i< queryPathParameterArrays[1].length)
                queryPathParameters[key] = queryPathParameterArrays[1][i]
        });
        this.queryPathParameters = queryPathParameters;
        this.updateAnyPathParameters();
        return this;
    },
    updateAnyParam: function () {
        if(this.query && this.fragment)
            this.anyParam = $H(this.query).merge(this.fragment);
        this.updateAnyPathParameters();
    },
    updateAnyPathParameters: function () {
        if(this.queryPathParameters && this.fragmentPathParameters)
            this.anyPathParameters = $H(this.queryPathParameters).merge(this.fragmentPathParameters);
        else
            this.anyPathParameters = $H(this.queryPathParameters) || $H(this.fragmentPathParameters) || $H({});
    },
    updateAnyState: function () {
        // ?foo or #foo
        this.anyParamState = this.hasAnyParameter() ? "" : this.fragmentString || this.queryString;
    },
    hasQuery: function () {
        return $H(this.query).values().compact().size() > 0;
    },
    hasPathQuery: function () {
        return $H(this.queryPathParameters).values().compact().size() > 0;
    },
    hasFragment: function () {
        return $H(this.fragment).values().compact().size() > 0;
    },
    hasPathFragment: function () {
        return $H(this.fragmentPathParameters).values().compact().size() > 0;
    },
    hasAnyParameter: function () {
        return this.hasQuery() || this.hasFragment();
    },
    hasAnyPathParameter: function () {
        return this.hasPathQuery() || this.hasPathFragment();
    },
    hasTarget: function () {
        return this.fragmentString.length > 0;
    },
    toString: function() {
        return this.urlString;
    },
    toPathFragment: function() {
        if($H(this.fragmentPathParameters).size() > 0)
            return $H(this.fragmentPathParameters).collect(function(pair){
                return pair[0]+"/"+ pair[1];
            }.bind(this)).join('/');
        else
            return "#";
    }
}));


